using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;
using BoOnce.Components;
using Microsoft.Xna.Framework.Graphics;
using BoOnce.Graphics;
using BoOnce.Xml;
using System.IO;
using BoOnce.Helpers;
using BoOnce.Physic;
using BoOnce.GameLogic.Entities.Drawable.Somebody;
using BoOnce.Core.Helpers;

namespace BoOnce.GameLogic.Entities
{
    public enum SomebodyPositionState { JUMP, CRUNCH, NORMAL };
    public enum SomebodyActionState { CAN_BE_INTERRUPT, BLOCK_OTHER_ACTION, QUEUE_OTHER_ACTION };

    abstract class Somebody : DrawableEntity, IPhysical
    {
        private const String SOMEBODY_DESCRIPTOR_NAME = "description.info";

        #region Attributes
        protected float _Height;
        protected float _Width;
        protected float _Mass;

        protected String _Name;
        protected String _FullName;

        protected float _Speed;
        protected int _Life;
        protected float _Strong;

        protected PhysicComponent _PhysicComponent;
        protected Dictionary<String, Punch> _Punchs;
        protected Dictionary<String, Animation> _Animations;

        private Animation _CurrentAnimation;
        protected SomebodyPositionState _PositionState = SomebodyPositionState.NORMAL;
        protected SomebodyActionState _ActionState = SomebodyActionState.CAN_BE_INTERRUPT;

        #endregion

        #region Constructor
        public Somebody(Vector2 pos, String name, float height, float width, float mass)
        {
            // General attributes
            _Name = name;
            _Height = height;
            _Width = width;
            _Speed = 10;
            _Mass = mass;

            _BoundingBox = new Rectangle((int)pos.X, (int)pos.Y, (int)width, (int)height);

            // Physical Attributes
            InitPhysics(pos);

            // Animations & Punch Attributes
            _Animations = new Dictionary<String, Animation>();
            _Punchs = new Dictionary<String, Punch>();
            LoadInformation(name);
            SetAnimation("idle", true, SomebodyPositionState.NORMAL, SomebodyActionState.CAN_BE_INTERRUPT);
        }
        #endregion

        
        #region Load XML Data
        private void LoadInformation(String name)
        {
            String root = Settings.CONTENT_PERSON_PATH + "/" + name;
            String file = Settings.CONTENT_ROOT_PATH + "/" + root + "/" + SOMEBODY_DESCRIPTOR_NAME;
            SomebodyDescriptionXml desc = (SomebodyDescriptionXml)XmlStorage.Load(file, typeof(SomebodyDescriptionXml));
            _Life = desc.Life;
            _FullName = desc.FullName;
            _Speed = desc.Speed;
            _Strong = desc.Strong;

            // Load Animation and Punch
            List<String> anims = desc.Animations;
            _Animations.Clear();
            String path;
            Animation anim;
            foreach (String animName in anims)
            {
                path = root + "/" + animName;
                anim = new Animation(path);
                _Animations.Add(anim.Name, anim);
                if (Punch.IsPunchAnimation(path))
                {
                    _Punchs.Add(anim.Name, new Punch(anim, path));
                }
            }
        }
        #endregion

        #region Animation
        public void SetAnimation(String name, bool loop, SomebodyPositionState posState, SomebodyActionState actionState)
        {
            Animation anim = GetAnimationByName(name);
            if (anim != null)
            {
                _CurrentAnimation = anim;
                _CurrentAnimation.Reset();
                _CurrentAnimation.Loop = loop;
                _CurrentAnimation.Play();
                _PositionState = posState;
                _ActionState = actionState;
            }
        }

        private Animation GetAnimationByName(String name)
        {
            Animation anim;
            if (_Animations.TryGetValue(name, out anim))
            {
                return anim;
            }
            else
            {
                Logger.WriteError("Animation:" + name + " is unknown for '" + _Name + "'");
                return null;
            }
        }
        #endregion

        #region IPhysical
        protected Body _Body;
        protected Geom _Geom;

        private Ground _Ground;
        private float _Accumulator;
        private Rectangle _BoundingBox;

        private void InitPhysics(Vector2 position)
        {
            _PhysicComponent = PhysicComponent.Instance;
            _Body = BodyFactory.Instance.CreateRectangleBody(_Width, _Height, _Mass);
            _Geom = GeomFactory.Instance.CreateRectangleGeom(_Body, _Width, _Height);
            Position = position;
            EnablePhysical(true);
            _Ground = _PhysicComponent.AddPhysicalEntity(_Body, _Geom, _Height);
            _Accumulator = 0f;
        }

        public Body Body { get { return _Body; } }

        public Geom Geom { get { return _Geom; } }

        public Rectangle BoundingBox { get { return _BoundingBox; } }

        public bool Intersect(IPhysical physic)
        {
            return _BoundingBox.Intersects(physic.BoundingBox);
        }

        public bool Intersect(Rectangle rect)
        {
            return _BoundingBox.Intersects(rect);
        }

        public void EnablePhysical(bool activated)
        {
            _Body.Enabled = activated;
            _Geom.CollisionEnabled = activated;
        }

        public Vector2 Position
        {
            get { return _Body.Position; }
            set
            {
                _Ground = _PhysicComponent.NotifyMove(value, _Ground, _Body, _Geom, _Height, ref _Accumulator);
                _BoundingBox.X = (int)(_Body.Position.X - _Width/2.0f);
                _BoundingBox.Y = (int)(_Body.Position.Y - _Height/2.0f);
            }
        }

        public Vector2 LinearVelocity
        {
            get { return _Body.LinearVelocity; }
        }

        public float Mass
        {
            get
            {
                return _Body.Mass;
            }
            set
            {
                _Body.Mass = value;
            }
        }

        public float Width
        {
            get { return _Width; }
        }

        public float Height
        {
            get { return _Height; }
        }
        #endregion

        #region Actions
        public virtual void DoAction(Action action, GameTime gameTime)
        {
            switch (action)
            {
                case Action.WalkRight:
                    Position += new Vector2((float)(gameTime.ElapsedGameTime.TotalMilliseconds * _Speed), 0.0f);
                    break;

                case Action.WalkLeft:
                    Position += new Vector2((float)(gameTime.ElapsedGameTime.TotalMilliseconds * -_Speed), 0.0f);
                    break;

                case Action.WalkUp:
                    Position += new Vector2(0.0f, (float)(gameTime.ElapsedGameTime.TotalMilliseconds * -_Speed));
                    break;

                case Action.WalkDown:
                    Position += new Vector2(0.0f, (float)(gameTime.ElapsedGameTime.TotalMilliseconds * _Speed));
                    break;

                default:
                    Logger.WriteWarning(this, "[" + _Name + "] Somebody.Move(" + action + ") is not yet support");
                    break;
            }
        }
        #endregion

        #region DrawableEntity
        public override void QuickDraw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            _CurrentAnimation.QuickDraw(gameTime, spriteBatch, Body.TotalRotation);
#if DEBUG
            if (DebugComponent.SHOW_SOMEBODY_STATES)
            {
                DebugComponent.Box(spriteBatch, Camera.ToScreenPosition(Position),
                    "Position", Position.ToString(),
                    //  "Name",_Name,
                    //  "FullName",_FullName,
                    "Animation Name", _CurrentAnimation.Name,
                    "Linear Velocity", _Body.LinearVelocity.ToString(),
                    "Angular Velocity", _Body.AngularVelocity.ToString(),
                    "Force", _Body.Force.ToString(), "CollideWith", _Geom.CollidesWith.ToString(),
                    "CollisionCat", _Geom.CollisionCategories.ToString()
                    );
            }
#endif
        }

        public override void Update(GameTime gameTime)
        {
            _CurrentAnimation.Position = Camera.ToScreenPosition(Position);
            _CurrentAnimation.Update(gameTime);

            if (_CurrentAnimation.IsFinished) SetAnimation("idle", true, _PositionState, SomebodyActionState.CAN_BE_INTERRUPT);
        }
        #endregion
    }
}
